/**
 * 
 */
package com.fengwk.support.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fengwk.support.exception.BaseException;
import com.fengwk.support.util.HttpClientUtils.ResponseEntity;
import com.fengwk.support.util.LoggerUtils.Logger;

/**
 * 工具类:获取操作系统属性
 */
public final class SystemUtils {
	
	private static final Logger LOG = LoggerUtils.getLogger(SystemUtils.class);
	
	/**
	 * 类路径前缀
	 */
	public static final String PREFIX_CLASSPATH = "classpath:";
	
	/**
	 * 未知系统
	 */
	public static final int SYSTEM_BIT_UNKNOWN = -1;
	/**
	 * 64位系统
	 */
	public static final int SYSTEM_BIT_64 = 64;
	/**
	 * 32位系统
	 */
	public static final int SYSTEM_BIT_32 = 32;
	
	/**
	 * 未知操作系统类型
	 */
	public static final int SYSTEM_TYPE_UNKNOWN = -1;
	public static final int SYSTEM_TYPE_WINDOWS = 1;
	public static final int SYSTEM_TYPE_LINUX = 2;
	public static final int SYSTEM_TYPE_MAC_OS = 3;
	public static final int SYSTEM_TYPE_MAC_OS_X = 4;
	public static final int SYSTEM_TYPE_OS2 = 5;
	public static final int SYSTEM_TYPE_SOLARIS = 6;
	public static final int SYSTEM_TYPE_SUN_OS = 7;
	public static final int SYSTEM_TYPE_MPEiX = 8;
	public static final int SYSTEM_TYPE_HP_UX = 9;
	public static final int SYSTEM_TYPE_AIX = 10;
	public static final int SYSTEM_TYPE_OS390 = 11;
	public static final int SYSTEM_TYPE_FREE_BSD = 12;
	public static final int SYSTEM_TYPE_IRIX = 13;
	public static final int SYSTEM_TYPE_DIGITAL_UNIX = 14;
	public static final int SYSTEM_TYPE_NET_WARE_411 = 15;
	public static final int SYSTEM_TYPE_OSF1 = 16;
	public static final int SYSTEM_TYPE_OPEN_VMS = 17;
	public static final int SYSTEM_TYPE_OTHERS = 18;
	
	private static final String SYSTEM_TYPE_WINDOWS_VALUE = "Windows";
	private static final String SYSTEM_TYPE_LINUX_VALUE = "Linux";
	private static final String SYSTEM_TYPE_MAC_OS_VALUE = "Mac OS";
	private static final String SYSTEM_TYPE_MAC_OS_X_VALUE = "Mac OS X";
	private static final String SYSTEM_TYPE_OS2_VALUE = "OS/2";
	private static final String SYSTEM_TYPE_SOLARIS_VALUE = "Solaris";
	private static final String SYSTEM_TYPE_SUN_OS_VALUE = "SunOS";
	private static final String SYSTEM_TYPE_MPEiX_VALUE = "MPE/iX";
	private static final String SYSTEM_TYPE_HP_UX_VALUE = "HP-UX";
	private static final String SYSTEM_TYPE_AIX_VALUE = "AIX";
	private static final String SYSTEM_TYPE_OS390_VALUE = "OS/390";
	private static final String SYSTEM_TYPE_FREE_BSD_VALUE = "FreeBSD";
	private static final String SYSTEM_TYPE_IRIX_VALUE = "Irix";
	private static final String SYSTEM_TYPE_DIGITAL_UNIX_VALUE = "Digital Unix";
	private static final String SYSTEM_TYPE_NET_WARE_411_VALUE = "NetWare";
	private static final String SYSTEM_TYPE_OSF1_VALUE = "OSF1";
	private static final String SYSTEM_TYPE_OPEN_VMS_VALUE = "OpenVMS";
	private static final String SYSTEM_TYPE_OTHERS_VALUE = "Others";
	
	// java缓存文件路径
	private static final String SYSTEM_TEMP_DIR = "java.io.tmpdir";
	// 系统默认编码
	private static final String SYSTEM_FILE_ENCODING = "file.encoding";
	// 系统默认语言
	private static final String SYSTEM_USER_LANGUAGE = "user.language";
	
	private static final String BLANK = "";
	
	private static final String OS_NAME = "os.name";
	private static final String OS_PROPERTIES = System.getProperties().getProperty(OS_NAME).toLowerCase();
	
	private static final String IP_INTERFACE = "http://ip.cn/";
	private static final String USER_AGENT_FLAG = "User-Agent";
	private static final String USER_AGENT_VIRTUAL_VALUE = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36";
	private static final String IP_SPLIT_REGEX = "<code>((\\d)+.){3}(\\d)+</code>";
	
	private SystemUtils(){
		throw new BaseException("can not instance " + getClass());
	}
	
	/**
	 * 是否windows系统
	 * 
	 * @return
	 */
	public static boolean isWindows(){
		return systemType() == SYSTEM_TYPE_WINDOWS;
	}
	
	/**
	 * 获取本地系统IP
	 * 
	 * @return 获取异常将抛出null
	 */
	public static String getLocalIp() {
		try {
			return InetAddress.getLocalHost().getHostAddress().toString();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 获取本机公网IP
	 * 
	 * @return 获取异常将抛出null
	 */
	public static String getInternetIp() {
		Map<String, String> headers = new HashMap<String, String>();
		headers.put(USER_AGENT_FLAG, USER_AGENT_VIRTUAL_VALUE);
		String body = null;
		ResponseEntity responseEntity = HttpClientUtils.get(IP_INTERFACE, null, headers);
		body = responseEntity.getStringContent();
		return StringUtils.trimHtmlTag(RegexUtils.regex(body, IP_SPLIT_REGEX));
	}
	
	/**
	 * 获取真实路径,使用默认类加载器实现{@link ClassUtils#getDefaultClassLoader()}
	 * 
	 * @param path 路径,若是类路径使用classpath:开头
	 * @return
	 */
	public static String getRealPath(String path) {
		return getRealPath(path, ClassUtils.getDefaultClassLoader());
	}
	
	/**
	 * 获取真实路径
	 * 
	 * @param path 路径,若是类路径使用classpath:开头
	 * @return
	 */
	public static <T> String getRealPath(String path, ClassLoader cl) {
		String realPath = null;
		if(isClasspath(path)) {
			path = StringUtils.trimPrefix(path, PREFIX_CLASSPATH);
			URL url = cl.getResource(path);
			realPath = url.getPath();
		} else {
			File f = FileUtils.buildFile(path);
			try {
				realPath = f.getCanonicalPath();
			} catch (IOException e) {
			    LOG.error(e);
			}
		}
		return realPath;
	}
	
	/**
	 * 获取类路径
	 * 
	 * @return
	 */
	public static String getClasspath() {
		return getRealPath(BLANK);
	}
	
	/**
	 * 类路径检查
	 * 
	 * @param path
	 * @return
	 */
	public static boolean isClasspath(String path) {
		return path.toLowerCase().startsWith(PREFIX_CLASSPATH);
	}

	/**
	 * 获取操作系统类型
	 * 
	 * @return
	 */
	public static int systemType() {
		if(isSystemType(SYSTEM_TYPE_WINDOWS_VALUE)) {
			return SYSTEM_TYPE_WINDOWS;
		}
		if(isSystemType(SYSTEM_TYPE_LINUX_VALUE)) {
			return SYSTEM_TYPE_LINUX;
		}
		if(isSystemType(SYSTEM_TYPE_MAC_OS_VALUE)) {
			return SYSTEM_TYPE_MAC_OS;
		}
		if(isSystemType(SYSTEM_TYPE_MAC_OS_X_VALUE)) {
			return SYSTEM_TYPE_MAC_OS_X;
		}
		if(isSystemType(SYSTEM_TYPE_OS2_VALUE)) {
			return SYSTEM_TYPE_OS2;
		}
		if(isSystemType(SYSTEM_TYPE_SOLARIS_VALUE)) {
			return SYSTEM_TYPE_SOLARIS;
		}
		if(isSystemType(SYSTEM_TYPE_SUN_OS_VALUE)) {
			return SYSTEM_TYPE_SUN_OS;
		}
		if(isSystemType(SYSTEM_TYPE_MPEiX_VALUE)) {
			return SYSTEM_TYPE_MPEiX;
		}
		if(isSystemType(SYSTEM_TYPE_HP_UX_VALUE)) {
			return SYSTEM_TYPE_HP_UX;
		}
		if(isSystemType(SYSTEM_TYPE_AIX_VALUE)) {
			return SYSTEM_TYPE_AIX;
		}
		if(isSystemType(SYSTEM_TYPE_OS390_VALUE)) {
			return SYSTEM_TYPE_OS390;
		}
		if(isSystemType(SYSTEM_TYPE_FREE_BSD_VALUE)) {
			return SYSTEM_TYPE_FREE_BSD;
		}
		if(isSystemType(SYSTEM_TYPE_IRIX_VALUE)) {
			return SYSTEM_TYPE_IRIX;
		}
		if(isSystemType(SYSTEM_TYPE_DIGITAL_UNIX_VALUE)) {
			return SYSTEM_TYPE_DIGITAL_UNIX;
		}
		if(isSystemType(SYSTEM_TYPE_NET_WARE_411_VALUE)) {
			return SYSTEM_TYPE_NET_WARE_411;
		}
		if(isSystemType(SYSTEM_TYPE_OSF1_VALUE)) {
			return SYSTEM_TYPE_OSF1;
		}
		if(isSystemType(SYSTEM_TYPE_OPEN_VMS_VALUE)) {
			return SYSTEM_TYPE_OPEN_VMS;
		}
		if(isSystemType(SYSTEM_TYPE_OTHERS_VALUE)) {
			return SYSTEM_TYPE_OTHERS;
		}
		return SYSTEM_TYPE_UNKNOWN;
	}
	
	private static boolean isSystemType(String systemType) {
		if(StringUtils.isBlank(OS_PROPERTIES)) {
			return false;
		}
		String[] _systemTypes = systemType.toLowerCase().split(" ");
		for(String s: _systemTypes) {
			if(!OS_PROPERTIES.contains(s)){
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 获取操作系统位数
	 * 
	 * @return
	 */
	public static int systemBit() {
		if(isWindows()) {
			String arch = System.getenv("PROCESSOR_ARCHITECTURE");
			String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
			if(arch.endsWith("64")|| wow64Arch != null && wow64Arch.endsWith("64")){
				return SYSTEM_BIT_64;
			}else {
				return SYSTEM_BIT_32;
			}
		}else if(systemType() == SYSTEM_TYPE_LINUX){
			try {
				Process process = Runtime.getRuntime().exec("getconf LONG_BIT");
				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
				String s=bufferedReader.readLine();
				if(s.contains("64")){
					return SYSTEM_BIT_64;
				}else {
					return SYSTEM_BIT_32;
				}
			}catch (IOException e) {
				return SYSTEM_BIT_UNKNOWN;
			}
		}
		return SYSTEM_BIT_UNKNOWN;
	}
	
	/**
	 * 获取MAC地址(延迟严重,不可用于服务器获取)
	 * 
	 * @param ip
	 * @return
	 */
	@Deprecated
	public static String getMacAddress(String ip) {
		String macAddress = "";
		macAddress = getMacInWindows(ip).trim();
		if (macAddress == null || "".equals(macAddress)) {
			macAddress = getMacInLinux(ip).trim();
		}
		return macAddress;
	}
	
	// windows系统获取mac地址
	private static String getMacInWindows(final String ip) {
		String result = null;
		List<String> resultList = exec(new String[][] {
			{"cmd", "/c", "ping " + ip},
			{"cmd", "/c", "arp -a"}
		});
		if (CollectionUtils.isNotBlank(resultList)) {
			result = filterMacAddress(ip, resultList.get(1), "-");
		}
		return result;
	}

	// linux系统获取mac地址
	private static String getMacInLinux(final String ip) {
		String result = null;
		List<String> resultList = exec(new String[][] {
			{"/bin/sh", "-c", "ping " + ip + " -c 2 && arp -a"}
		});
		if (CollectionUtils.isNotBlank(resultList)) {
			result = filterMacAddress(ip, resultList.get(1), ":");
		}
		return result;
	}
	
	// 从命令结果中获取MacAddress
	private static String filterMacAddress(final String ip, final String sourceString, final String macSeparator) {
		String result = "";
		String regExp = "((([0-9,A-F,a-f]{1,2}" + macSeparator + "){1,5})[0-9,A-F,a-f]{1,2})";
		Pattern pattern = Pattern.compile(regExp);
		Matcher matcher = pattern.matcher(sourceString);
		while (matcher.find()) {
			result = matcher.group(1);
			if (sourceString.indexOf(ip) <= sourceString.lastIndexOf(matcher.group(1))) {
				break; // 如果有多个IP,只匹配本IP对应的Mac.
			}
		}
		return result;
	}
	
	/**
	 * 在指定环境的单独进程中执行指定的字符串命令
	 * 
	 * @param cmdarrays
	 * @return 失败时返回null
	 */
	public static Process exec(String[] cmdarrays) {
		Runtime runtime = Runtime.getRuntime();
		try {
			return runtime.exec(cmdarrays);
		} catch (IOException e) {
		    LOG.error(e);
		}
		return null;
	}
	
	/**
	 * 执行一系列的cmd命令
	 * 
	 * @param cmds
	 * @return
	 */
	public static List<String> exec(String[]...cmdarrays) {
		List<String> resultList = new ArrayList<>();
		if (ArrayUtils.isNotBlank(cmdarrays)) {
			for (String[] cmdarray: cmdarrays) {
				try {
					Process proc = exec(cmdarray);
					// 导致当前线程等待，如有必要，一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程，此方法立即返回。如果没有终止该子进程，调用的线程将被阻塞，直到退出子进程
					proc.waitFor();
					resultList.add(IoUtils.toString(proc.getInputStream(), true));
				} catch (InterruptedException e) {
				    LOG.error(e);
				}
			}
		}
		return resultList;
	}
	
	/**
	 * 获取系统属性
	 * 
	 * @param key
	 * @return
	 */
	public static String getSystemProperty(String key) {
		return System.getProperty(key);
	}
	
	/**
	 * 获取临时文件路径,最后有带有File.separator
	 * 
	 * @return
	 */
	public static String getTempDir() {
		String tempDir = getSystemProperty(SYSTEM_TEMP_DIR);
		String separator = tempDir.substring(tempDir.length() - 1);
		if (!separator.equals(File.separator))
			tempDir += File.separator;
		return tempDir;
	}
	
	/**
	 * 获取系统默认编码
	 * 
	 * @return
	 */
	public static String getFileEncoding() {
		return getSystemProperty(SYSTEM_FILE_ENCODING);
	}
	
	/**
	 * 获取系统默认语言
	 * 
	 * @return
	 */
	public static String getUserLanguage() {
		return getSystemProperty(SYSTEM_USER_LANGUAGE);
	}
	
	/**
	 * 获取系统可用核心数
	 * 
	 * @return
	 */
	public static int availableProcessors() {
		return Runtime.getRuntime().availableProcessors();
	}
	
}
